<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>通话</title>

    <style>
      .userlist {
        width: 200px;
        height: 300px;
        border: 1px solid rgb(0, 0, 0);
      }
      .userlist p {
        margin: 0;
        padding: 0;
        border: 1px solid #ededed;
        cursor: pointer;
        display: flex;
        align-items: stretch;
        line-height: 20px;
        vertical-align: middle;
      }
      * {
        box-sizing: border-box;
      }
      .handler {
        margin-left: 30px;
      }
      .handler button {
        margin: 0;
      }
      .view {
        display: flex;
        padding-top: 20px;
      }

      .userlist p.active::after {
        content: "";
        width: 4px;
        background-color: rgb(0, 145, 255);
        margin-left: auto;
        margin-right: 0;
      }

      .video-box {
        border: 1px solid red;
        margin-top: 30px;
      }

      #inputUsername {
        font-size: 18px;
        line-height: 1.2;
      }
      button {
        font-size: 18px;
      }
      #testBtn {
        margin-top: 10px;
      }

      .contentBox {
        flex: 1;
        border: 1px solid black;
        margin-left: 50px;
        height: 400px;
        overflow: auto;
      }
    </style>
  </head>
  <body>
    <h1>webRTC Demo</h1>
    <div class="entry">
      <input
        style="
          line-height: 1.5;
          width: 500px;
          font-size: 18px;
          margin-bottom: 10px;
        "
        type="text"
        value="http://192.168.1.106:3000"
        id="serverIP"
        placeholder="服务器地址"
      />
    </div>
    <div class="entry">
      <input type="text" id="inputUsername" placeholder="输入名称" />
      <button id="login">登录</button>
      <!-- <button id="leave">离开</button> -->
    </div>
    <div class="view">
      <div class="userlist" id="userlist"></div>
      <div class="handler">
        <button id="call">视频</button>
        <br />
        <button id="testBtn">测试</button>
      </div>
      <div class="contentBox">
        <pre style="white-space: pre-wrap" id="setViewContent"></pre>
      </div>
    </div>

    <div class="video-box" id="videoBox" style="display: block">
      <video id="localVideo" autoplay muted playsinline>本地</video>
      <br />
      <video id="remoteVideo" autoplay playsinline>远端</video>
    </div>

    <!-- <script src="/socket.io/socket.io.js"></script> -->

    <script type="module">
      import { io } from "https://cdn.socket.io/4.7.5/socket.io.esm.min.js";
      const serverIP = document.getElementById("serverIP");
      const login = document.getElementById("login");
      const inputUsername = document.getElementById("inputUsername");
      const localVideo = document.getElementById("localVideo");
      const remoteVideo = document.getElementById("remoteVideo");
      const userlistDom = document.getElementById("userlist");
      const callDom = document.getElementById("call");
      const videoBox = document.getElementById("videoBox");
      const testBtn = document.getElementById("testBtn");
      const setViewContent = document.getElementById("setViewContent");

      testBtn.onclick = () => {
        videoBox.style.display =
          videoBox.style.display == "block" ? "none" : "block";
      };
      let remoteUserId = -1; // 对端
      let isReceive = false; // 代表被邀请人是否接受
      let nowUser = null;
      let receive = null;

      let localStream = null; //本地媒体流
      let remoteStream = null; //远端媒体流

      let socket = null;

      let pc = null;

      //创建RTCPeerConnection
      function createPeerConnection() {
        pc = new RTCPeerConnection(null);
        pc.onicecandidate = handleIceCandidate;
        pc.ontrack = handleRemoteStreamAdd;
        if (localStream) {
          localStream
            .getTracks()
            .forEach((track) => pc.addTrack(track, localStream));
        }
      }

      //ice候选项方法
      function handleIceCandidate(e) {
        if (e.candidate) {
          if (remoteUserId != 1) {
            return;
          }

          socket.emit("candidate", {
            user: receive,
            candidateJson: {
              label: e.candidate.sdpMLineIndex,
              id: e.candidate.sdpMid,
              candidate: e.candidate.candidate,
            },
          });
        }
      }

      //获取远端媒体流处理
      function handleRemoteStreamAdd(e) {
        remoteStream = e.streams[0];
        console.log("远端媒体流", remoteStream);
        remoteVideo.srcObject = remoteStream;
      }

      login.onclick = () => {
        console.log(serverIP.value);
        const thisUserInfo = {
          username: inputUsername.value,
          now: Date.now(),
          avatar: `https://api.dicebear.com/7.x/miniavs/svg?seed=${
            Math.floor(Math.random() * 10) + 1
          }`,
        };
        const socketIo = io("http://192.168.1.106:3000", {
          query: thisUserInfo,
        });
        socket = socketIo;

        socketIo.on("connect", () => {
          console.log("连接成功~~~", socketIo.id);
          nowUser = {
            ...thisUserInfo,
            socketId: socketIo.id,
          };
        });

        socketIo.on("update-userlist", (userInfo) => {
          const { userlist } = userInfo;
          console.log("update-userlist", userlist);
          userlist.forEach((item) => {
            const pDom = document.createElement("p");
            let items = item;
            pDom.id = item.socketId;
            pDom.classList.add("user-item");
            pDom.innerText = item.username;
            userlistDom.appendChild(pDom);
            pDom.addEventListener("click", () => {
              const itemsP = document.querySelectorAll(".user-item");
              itemsP.forEach((item) => {
                item.classList.remove("active");
              });
              pDom.classList.add("active");
              receive = items;
            });
          });
        });
        socket.on("user-logout", (userinfo) => {
          // userinfo.socketId
          const pDom = document.getElementById(userinfo.socketId);
          pDom?.remove();
          pc.close();
          localStream.getTracks().forEach((track) => {
            track.stop();
          });
          pc = null;
          receive = null;
          localStream = null;
          remoteStream = null;

          //置空对方和本地的媒体流
          remoteVideo.srcObject = null;
          localVideo.srcObject = null;
        });

        socket.on("peer-offer", async (res) => {
          console.log("B端peer-offer", res);
          const { offer, user } = res;
          const boo = confirm("是否同意视频");
          if (!boo) return;
          receive = user;
          // localStream = await navigator.mediaDevices.getUserMedia({
          //   audio: false,
          //   video: true,
          // });
          createPeerConnection();
          // localVideo.srcObject = localStream;
          // B端添加 offer
          pc.setRemoteDescription(offer);
          // B端生成 answer
          const answer = await pc.createAnswer();
          // 本地再设置answer
          await pc.setLocalDescription(answer);
          socket.emit("answer", {
            answer,
            user,
          });
        });

        //A端接收到 answer
        socket.on("peer-answer", (res) => {
          console.log("A端收peer-answer", res);
          // 到这里应该是A端
          const { answer } = res;
          // 本地再设置 answer
          pc.setRemoteDescription(answer);
        });

        // 接收远程过来的 offer 后续
        socket.on("peer-candidate", (res) => {
          console.log("客户端接收peer-candidate", res);
          // const pc = RTCpc;
          const ice = new RTCIceCandidate({
            candidate: res.candidateJson.candidate,
            sdpMid: res.candidateJson.id,
            sdpMLineIndex: res.candidateJson.label,
          });

          pc?.addIceCandidate(ice);
        });
      };

      callDom.onclick = async () => {
        // if (!nowUser) {
        //   console.warn("未登录");
        //   return;
        // }
        // if (!receive) {
        //   console.warn("未选择");
        //   return;
        // }

        remoteUserId = 1; //随便写一个
        // localStream = await navigator.mediaDevices.getUserMedia({
        //   audio: true,
        //   video: true,
        // });

        // 列出相机和麦克风。
        navigator.mediaDevices
          .enumerateDevices()
          .then(function (devices) {
            devices.forEach(function (device) {
              console.log(
                device.kind + ": " + device.label + " id = " + device.deviceId
              );
            });
            setViewContent.innerHTML += JSON.stringify(devices,null,2);
          })
          .catch(function (err) {
            console.log(err.name + ": " + err.message);
          });
          return

        localStream = await navigator.mediaDevices.getDisplayMedia({
          audio: true,
          video: true,
        });

        createPeerConnection();
        localVideo.srcObject = localStream;

        const offer = await pc.createOffer();
        await pc.setLocalDescription(offer); //本地设置offer
        // 发送给B端 offer
        socket.emit("offer", {
          user: receive,
          offer,
        });
      };
    </script>
  </body>
</html>
